Generic types form a powerful and ubiquitous aspect of object-oriented programming. Generic types, also known as parameterized types, define formal constructs of some type(s) (e.g., a list with elements of some type, a queue with elements of some type, a stack with elements of some type . . . ). This “some type(s)” is referred to in the art as the parameter(s) of generic types. A common notation for generic types is List<T>, where T represents the type parameter and List is the generic type. When a concrete type is used as a type parameter of a generic type, the resulting type is commonly referred to as generic instantiation or constructed type (e.g., a stack of integer values Stack<int>, list of strings List<string>, queue of floating-point values Queue<float> . . . ).
In many cases, the set of types that can be used for instantiation of a given generic type needs to be restricted. For instance, to be used for instantiation of a sorted list, SortedList<T>, instances of the type parameter T must be comparable to other instances of T, otherwise sorting would be impossible. In other words, type T should implement interface IComparable<T>, for example, which exposes a compare function—in C# notation: SortedList<T> where T: IComparable<T>. Another common constraint is to restrict type parameters to types that have a default constructor—in C# notation: Dictionary<K, V> where K: IComparable<K> where V: New( ). Such limitations on type parameters are called constraints.
Different execution environments (e.g., Common Language Runtime (CLR), Java Runtime Environment (JRE) . . . ) employ disparate means to specify the constraints and different sets of possible constraints, however they all implement a set of possible constraints that is predefined and built into the execution environment (i.e., hard coded). For example, the CLR implementation of generics permits a demand that a generic type parameter be derived from a certain type (e.g., extension constraint), implements certain interface(s) (e.g., implementation constraint), has a default constructor (e.g., constructor constraint) and is a reference type or value type. These constraints are represented in metadata via spare bits in two generic parameter tables.
Predefined constraints of these kinds can be utilized in certain combinations or not at all, but there is no possibility of introducing a new kind or type of constraint without redesigning and/or rebuilding the execution environment/infrastructure. In particular, additional space must be allocated (if even available) to express new constraints and a completely new support infrastructure must be built to enable the engine to make decisions regarding each new constraint. Stated differently, conventional constraints are hard-wired into the execution environment resulting in a non-extensible system. This leads to problems ranging from inconvenience when it is cumbersome but possible to express a new kind of constraint through a combination of existing constraints, to language inadequacy, when it is not possible to express a particular new constraint.
Accordingly, there is a need in the art for a system and method of constraining generic type parameters that is simple and efficient as well as easily extensible. More specifically, there is a need for a mechanism that does not require rebuilding an associated engine/infrastructure when adding new forms of constraints.